package org.dfl.plugin.editor.commands;

import java.util.Collection;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;
import org.dfl.core.model.TInputEdge;
import org.dfl.core.model.TOutputEdge;
import org.dfl.core.utils.Utils;
import org.dfl.plugin.dialogs.TransitionPropertiesDialog;
import org.dfl.plugin.dialogs.TransitionPropertiesDialogFactory;
import org.dfl.plugin.editor.model.ConnectionModelElement;
import org.dfl.plugin.editor.model.TransitionModelElement;
import org.eclipse.gef.commands.Command;
import org.jdom.Element;

/**
 * command that edit transition properties
 * @author Piotr Wlodarczyk
 *
 */
public class TransitionEditPropertiesCommand extends Command {
	private static Logger logger = Logger.getLogger(TransitionEditPropertiesCommand.class);
	
	private TransitionModelElement tme;
	private Element oldTransitionElem;
	private Element newTransitionElem;
	private Collection<TInputEdge> oldInputEdges;
	private Set<TOutputEdge> oldOutputEdges;
	private Collection<TInputEdge> newInputEdges;
	private Set<TOutputEdge> newOutputEdges;

	public TransitionEditPropertiesCommand(TransitionModelElement tme) {
		super();
		this.tme = tme;
	}
	
	@Override
	public boolean canExecute() {
		if(tme.getDiagram() == null || !tme.getDiagram().isEditable()) 
			return false;
		return true;
	}
	
	@Override
	public void execute() {
		try{
			oldTransitionElem = tme.getTransition().save();
			oldInputEdges = tme.getTransition().getInputEdges();
			oldOutputEdges = tme.getTransition().getOutputEdges();
			
			newInputEdges = tme.getTransition().getInputEdges();
			newOutputEdges = tme.getTransition().getOutputEdges();
			
			TransitionPropertiesDialog dialog = TransitionPropertiesDialogFactory.getTransitionPropertiesDialog(tme);
			dialog.open();
			if(dialog.isOkButtonClicked()){
				newTransitionElem = tme.getTransition().save();
				
				if(Utils.equals(oldTransitionElem,newTransitionElem)){
					undo();
					throw new CancelCommandRuntimeException();
				}

				List<ConnectionModelElement> conns = tme.getSourceConnections();
				for(ConnectionModelElement conn : conns)conn.setRefreshDiagramEnabled(false);
				conns = tme.getTargetConnections();
				for(ConnectionModelElement conn : conns)conn.setRefreshDiagramEnabled(false);

				
				// disconnect all input and output edges
				for(TInputEdge e : oldInputEdges)e.disconnect();
				for(TOutputEdge e : oldOutputEdges) e.disconnect();
				
				tme.getTransition().load(newTransitionElem);
				
				// try to reconnect all input and output edges
				for (TInputEdge e : oldInputEdges) {
					try {
						e.reconnect();
					} catch (Exception e1) {}
				}
				for (TOutputEdge e : oldOutputEdges){
					try {
						e.reconnect();
					} catch (Exception e1) {}
				}
				
				newInputEdges = tme.getTransition().getInputEdges();
				newOutputEdges = tme.getTransition().getOutputEdges();

				conns = tme.getSourceConnections();
				for(ConnectionModelElement conn : conns)conn.setRefreshDiagramEnabled(true);
				conns = tme.getTargetConnections();
				for(ConnectionModelElement conn : conns)conn.setRefreshDiagramEnabled(true);
				
				if(oldTransitionElem.equals(newTransitionElem)){
					tme = null;
					throw new CancelCommandRuntimeException();
				}
				tme.modelStructureChanged();
				tme.refreshMarkers();
			}else{
				undo();
				throw new CancelCommandRuntimeException();
			}
		} catch (CancelCommandRuntimeException e) {
			throw e;
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
	}
	
	@Override
	public boolean canUndo() {
		if(tme.getDiagram() == null || !tme.getDiagram().isEditable()) 
			return false;
		return super.canUndo();
	}
	
	@Override
	public void undo() {
		try {
			List<ConnectionModelElement> conns = tme.getSourceConnections();
			for(ConnectionModelElement conn : conns)conn.setRefreshDiagramEnabled(false);
			conns = tme.getTargetConnections();
			for(ConnectionModelElement conn : conns)conn.setRefreshDiagramEnabled(false);
			
			// try to reconnect all input and output edges
			for(TInputEdge e : newInputEdges) e.disconnect();
			for(TOutputEdge e : newOutputEdges) e.disconnect();
			
			tme.getTransition().load(oldTransitionElem);
			
			// try to reconnect all input and output edges
			for(TInputEdge e : oldInputEdges) e.reconnect();
			for(TOutputEdge e : oldOutputEdges) e.reconnect();

			conns = tme.getSourceConnections();
			for(ConnectionModelElement conn : conns)conn.setRefreshDiagramEnabled(true);
			conns = tme.getTargetConnections();
			for(ConnectionModelElement conn : conns)conn.setRefreshDiagramEnabled(true);
			
			tme.modelStructureChanged();
			tme.refreshMarkers();
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
	}
	
	@Override
	public void redo() {
		try {
			List<ConnectionModelElement> conns = tme.getSourceConnections();
			for(ConnectionModelElement conn : conns)conn.setRefreshDiagramEnabled(false);
			conns = tme.getTargetConnections();
			for(ConnectionModelElement conn : conns)conn.setRefreshDiagramEnabled(false);
			
			// try to reconnect all input and output edges
			for(TInputEdge e : oldInputEdges) e.disconnect();
			for(TOutputEdge e : oldOutputEdges) e.disconnect();
			
			tme.getTransition().load(newTransitionElem);
			
			// try to reconnect all input and output edges
			for(TInputEdge e : newInputEdges) e.reconnect();
			for(TOutputEdge e : newOutputEdges) e.reconnect();
			
			conns = tme.getSourceConnections();
			for(ConnectionModelElement conn : conns)conn.setRefreshDiagramEnabled(true);
			conns = tme.getTargetConnections();
			for(ConnectionModelElement conn : conns)conn.setRefreshDiagramEnabled(true);
			
			tme.modelStructureChanged();
			tme.refreshMarkers();
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
	}
}
